﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace CWM.Skinn
{
    public static partial class SkinnEx
    {
        public static AvatarPose CreateHumaoidAvatar(this Animator tpose, Animator model, AvatarSettings settings = null)
        {
            var avatarPose = new AvatarPose();
            var transforms = model.GetComponentsInChildren<Transform>();
            var tposeTransforms = tpose.GetComponentsInChildren<Transform>();
            var bones = new AvatarPose.Bone[transforms.Length];
            var boneIndices = new List<int>();

            for (int i = 0; i < bones.Length; i++)
            {
                bones[i] = new AvatarPose.Bone();
                if (!transforms[i]) continue;
                bones[i].m_boneName = transforms[i].name;
                bones[i].m_humanBoneName = GetHumanBoneName(tpose, transforms[i]);

                bones[i].m_position = transforms[i].localPosition;
                bones[i].m_rotation = transforms[i].localRotation;
                bones[i].m_scale = transforms[i].localScale;

                var boneIndex = tposeTransforms.GetIndex(transforms[i].name, true);
                if (boneIndex > -1)
                {
                    bones[i].m_position = tposeTransforms[boneIndex].localPosition;
                    bones[i].m_rotation = tposeTransforms[boneIndex].localRotation;
                    bones[i].m_scale = tposeTransforms[boneIndex].localScale;
                }
                if (bones[i].m_humanBoneName != "") boneIndices.Add(i);
            }
            avatarPose.bones = bones;

            avatarPose.humanoidBones = boneIndices.ToArray();
            var humanBones = new HumanBone[boneIndices.Count];
            for (int i = 0; i < humanBones.Length; i++)
            {
                humanBones[i] = new HumanBone
                {
                    humanName = bones[boneIndices[i]].m_humanBoneName,
                    boneName = bones[boneIndices[i]].m_boneName
                };
                humanBones[i].limit.useDefaultValues = true;
            }

            if (humanBones.Length < 15)
            {
                Debug.LogWarning(humanBones.Length + "Humanoid Bones, No Avatar Created.");
                return null;
            }

            var skeletalBones = new SkeletonBone[bones.Length];
            for (int i = 0; i < skeletalBones.Length; i++)
            {
                skeletalBones[i] = new SkeletonBone
                {
                    name = bones[i].m_boneName,
                    position = bones[i].m_position,
                    rotation = bones[i].m_rotation,
                    scale = bones[i].m_scale
                };
            }

            var avatarSettings = AvatarSettings.FallbackToDefualtValue(settings);
            var humanDescription = new HumanDescription
            {
                human = humanBones,
                skeleton = skeletalBones,
                upperArmTwist = avatarSettings.upperArmTwist,
                lowerArmTwist = avatarSettings.lowerArmTwist,
                upperLegTwist = avatarSettings.upperLegTwist,
                lowerLegTwist = avatarSettings.lowerLegTwist,
                armStretch = avatarSettings.armStretch,
                legStretch = avatarSettings.legStretch,
                feetSpacing = avatarSettings.feetSpacing,
                hasTranslationDoF = avatarSettings.hasTranslationDoF,
            };

            var newAvatar = AvatarBuilder.BuildHumanAvatar(model.gameObject, humanDescription);
            newAvatar.name = tpose.gameObject.name + "_Avatar";
            avatarPose.m_avatar = newAvatar;
            return avatarPose;
        }

        public static string GetHumanBoneName(this Animator animator, Transform transform)
        {
            if (animator == null || !animator.isHuman || !transform) return "";
            foreach (HumanBodyBones humanBone in Enum.GetValues(typeof(HumanBodyBones)))
            {
                if (humanBone == HumanBodyBones.LastBone) break;
                var hTransform = animator.GetBoneTransform(humanBone);
                if (hTransform == null) continue;
                if (transform.name == hTransform.name) return humanBone.ToString();
            }
            return "";
        }
    }
}